package com.lang.object;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * 用于测试java lang 包里面的类
 * Created by yangliwei on 2017/7/17.
 */
public class ObjectDemo1 {

    /**
     * 测试Object里面的clone方法,引用的对象无法copy
     */
    @Test
    public void test01() throws CloneNotSupportedException {

        D d = new D();
        d.setD(4);
        d.setDname("D");
        C c = new C();
        c.setC(3);
        c.setCname("C");
        c.setB(2);
        c.setBname("B");
        c.setA(1);
        c.setAname("A");
        c.setD(d);

        C cloneObject = (C) c.clone();
        System.out.println(cloneObject);
    }

    /**
     * 测试hashCode方法
     */
    @Test
    public void testHashCode() {
        String strA = "a";
        String newA = new String("a");
        Integer intA = 97;
        System.out.println("两个对象不相等但是hashCode一样：");
        System.out.println(strA.equals(intA));
        System.out.println(strA.hashCode()== intA);

        System.out.println("==与equal区别，两个对象内容一样但物理地址不一样，则eqaul相等，==不相等");
        System.out.println(newA.equals(strA));
        System.out.println(newA==strA);


        System.out.println("两个新对象hash与equal都不相等");
        Object obj1 = new Object();
        Object obj2 = new Object();
        System.out.println(obj1.equals(obj2));
        System.out.println(obj1.hashCode()==obj2.hashCode());

    }

    /**
     * getClass()
     * 返回该对象所表示的类
     */
    @Test
    public void testGetClass() {
        Number n = 1;
        Class<? extends Number> nClass = n.getClass();
        System.out.println(nClass.getName());
    }

    /**
     * 在对象在被回收前肯定会调用一次，但调用了对象不一定马上被回收，可以用来在判断对象是否还有用，如果有用可以把对象clone,
     * 想法：可以用在数据库的保存上面，当高并发时，可以把数据放在队列里面，当发生gc的时候观察是否要拯救这个对象
     * 不推荐使用
     */
    @Test
    public void testFinalize() {
        Integer i = new Integer(1);
        System.gc();
    }

    /**
     * 测试父子类的引用关系
     */
    @Test
    public void extentTest(){
        //父类引用实际对象可能是父类也可能是子类，此时o可以引用任何其子类对象
        Object o = new Object();
        System.out.println(o.getClass());//class java.lang.Object
        o = new String();
        System.out.println(o.getClass());//class java.lang.String
        o = new Integer(1);
        System.out.println(o.getClass());//class java.lang.Integer

        //变量名实际代表的对象由实际初始化时的对象决定的，此时os表示Object数组
        Object[] os = new Object[5];//os代表的对象为  [Ljava.lang.Object;可以放置任何对象
        System.out.println(os.getClass());
        os[0] = new Integer(1);
        os[1] = "ss";
        System.out.println(os[0].getClass());
        System.out.println(os[1].getClass());

        //此时os表示String数组，若此时在里面放置Integer对象会报错
        os = new String[5];
        System.out.println(os.getClass()); //os代表的对象为  [Ljava.lang.Object;
        os[0] = new Integer(1);//放置Integer对象报错
        os[1] = "ss";

        //将一个 Collection 对象转化为Object[] 需要进行如下判断，此为java8及以下的bug
        // bug链接: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
        // 参考链接: https://blog.csdn.net/aitangyong/article/details/30274749
        Collection c = Arrays.asList("ss","bb");
        System.out.println(c.getClass());//此时c的实际对象为 java.util.Arrays.ArrayList,里面的toArray()方法使用的时clone()会导致实际a的类型部位Object[]
        Object[] a = c.toArray();
        // If c.toArray incorrectly doesn't return Object[], copy it.
        if (a.getClass() != Object[].class)
            a = Arrays.copyOf(a, a.length, Object[].class);
    }

    /**
     * 测试泛型
     */
    @Test
    public void testGenerics(){
        //根据extentTest测试 ? super B可以为Object所以里面可以田间任意B及B子类
        List<? super B> bSuper = new ArrayList<>();
        bSuper.add(new B());
        bSuper.add(new C());

        List<? extends B> bChild = new ArrayList<>();
//        bChild.add(new B()); //此时无法添加任何元素，因为无法知道数组中实际对象是B的哪个子类

    }

    /**
     * 测试多态，实际根据方法参数类型进行匹配具体的方法签名，所以重载方法中的参数尽量不要使用继承关系。在编译器就确认了具体方法。
     * 测试多态，重载实际根据参数类型进行匹配具体的方法签名(编译器就绑定了)，所以重载方法中的参数尽量不要使用继承关系，会产生歧义（com.util.list.ListDemo#testRemove()）。重写是在运行时根据实际类型进行绑定方法的。
     */
    @Test
    public void testDuotai(){
        A a = new A();
        test(a);//test(A a)
        a = new B();
        test(a);//test(A a)
        B b = new B();
        test(b);
    }

    public void test(A a){
        System.out.println(a.getClass());
        System.out.println("call parent A");
    }
    public void test(B b){
        System.out.println(b.getClass());
        System.out.println("call child B");
    }

}


/*
 clone()
	1.首先Object没有实现Cloneable接口，所以在类为Object调用clone方法会抛出异常.
	2.这个方法的访问权限是protected，只能在子类中使用，或者在同包内使用。
	3.关于浅copy和深copy问题，默认是浅copy
	4.一般子类重写cloen方法后最好选择把权限变为pulbic
	5.如果要实现深copy需要把引用对象显式clone，

equals()
	1.只有当两个对象的引用相等时才返回true，和==相同
	2.重写该方法时，通常需要重写hashCode方法

hashCode()
	1.一般情况下要求两个相同的对象调用hashCode返回的值一样，底层实现是根据对象地址计算一个整数。


getClass()
	1.返回该对象所表示的类

finalize()
	1.在对象被回收前肯定会被调用一次，但是调用了对象不一定马上被回收，可以通过在此方法中判断对象是否该被回收，如果对象还有用可以把对象clone出来。但是finalize方法只会执行一次，第二次就不能自救了。
	2.finalize方法针对的是对象，当对象没有被引用的时候才会被回收。
	3.不推荐使用


wait()
	把当前线程扔进等待队列
notify()
	从等待队列里面随机获取一个扔进进程



 */